#include "common.hpp"

static const char *optString = "r:i:h?";

static const struct option longOpts[] = {
	{ "help", no_argument, NULL, 'h' },
	{ "index", required_argument, NULL, 'i' },
	{ "resource", required_argument, NULL, 'r' },
	{ 0,0,0,0 }
};

int main (int argc, char* const argv[]) {
	srand( time(NULL) + getpid() );

	ipc::shared_memory_with_exceptions<size_t> sync ( my_ftok(COUNTER|SYNC) );

	ipc::semaphore_with_exceptions mutex_sync ( my_ftok(MUTEX|SYNC),0 );

	ipc::messages_queue_with_exceptions transportadoraA_salida ( my_ftok(MSG_QUEUE|TRANSPORTADORA_A) );
	ipc::messages_queue_with_exceptions llenadoras_salida ( my_ftok(MSG_QUEUE|LLENADORA_0) );
	ipc::messages_queue_with_exceptions repositor_salida ( my_ftok(MSG_QUEUE|REPOSITOR) );

	int longIndex = 0;
	int option = 0;
	bool ayuda = false;
	bool seguir = true;
	size_t procesadas = 0;
	long jugo = 0;
	int boca = -1;
	bool azucaradora_demorada = false;

	// Interpretación de parametros.
	while ( (option = getopt_long(argc,argv,optString,longOpts,&longIndex) ) != EOF ) {
		switch (option) {
			case '?':
			case 'h':
			case ':':
				ayuda = true;
				break;
			case 'i':
				boca = parse<size_t>(optarg,-1);
				if (boca >= MAX_LLENADORAS)
					boca = -1;
				break;
			case 'r':
				jugo = parse<long>(optarg,-1);
			default:
				break;
		}
	}

	if ( ayuda || boca < 0 || jugo < 0) {
		std::cerr << "Proceso de control de un brazo con bocas de llenado de jugo." << std::endl;
		std::cerr << "\tUso: " << argv[0] << " [-h] <-index natural> [-r natural]" << std::endl;
		std::cerr << "\tParametros:" << std::endl;
		std::cerr << "\t\t-h,--help:\tMostrar este mensaje de ayuda." << std::endl;
		std::cerr << "\t\t-i,--index:\tNúmero se semaforo a utilizar." << std::endl;
		std::cerr << "\t\t-r,--resource:\tNúmero de dosis de jugo disponibles en la estación." << std::endl;

		return (ayuda) ? 0 : -1;
	}

	// Recien en este momento se que boca de la llenadora utilizo.
	ipc::semaphore_with_exceptions mutex_llenadora (my_ftok(MUTEX|LLENADORA_0),boca);
	ipc::semaphore_with_exceptions mutex_azucaradora (my_ftok(MUTEX|AZUCARADORA_0),boca);

	sBotella llena;
	llena.tipo = BOTELLA;
	llena.estado = LLENA;

	sPedido pedir_botella;
	pedir_botella.tipo = PEDIR_RECURSO;
	pedir_botella.origen = getpid();
	pedir_botella.recurso = BOTELLA;
	pedir_botella.sem_key = my_ftok(MUTEX|LLENADORA_0);
	pedir_botella.sem_num = boca;

	sPedido pedir_jugo;
	pedir_jugo.tipo = PEDIR_RECURSO;
	pedir_jugo.origen = getpid();
	pedir_jugo.recurso = JUGO;
	pedir_jugo.sem_key = my_ftok(MUTEX|LLENADORA_0);
	pedir_jugo.sem_num = boca;

	sBotella entrante;
	sReposicion reposicion;

	const std::string msg_header = "Estación de llenado " + parse(boca) + ": ";
	logger.setColor("\033[0;37m");
	try {
		transportadoraA_salida.get(PERMITS);
		llenadoras_salida.get(PERMITS);
		repositor_salida.get(PERMITS);

		mutex_llenadora.get(PERMITS);
		mutex_azucaradora.get(PERMITS);
		mutex_sync.get(PERMITS);
		
		sync.get(PERMITS,MAX_LLENADORAS);

		logger << msg_header << "Inicio." << nl;
		procesadas = 0;
		while (seguir) {
			mutex_llenadora.wait();

			if (jugo > 0) {
				logger << msg_header << "Esperando botella vacia." << nl;
				transportadoraA_salida.send(pedir_botella);
				mutex_llenadora.wait();
				entrante.tipo = 0;
				transportadoraA_salida.receive(entrante,getpid());

				if ( (entrante.tipo == getpid()) && (entrante.estado == VACIA) ) {
					logger << msg_header << "Sincronizando con azucaradora." << nl;
					// Preprotocolo de sincronización con la azucaradora.
					azucaradora_demorada = false;
					mutex_sync.wait();
						if (sync[boca] == 0)
							sync[boca] = 1;
						else
							sync[boca]--;
						azucaradora_demorada = (sync[boca] != 0);
					mutex_sync.signal();
					if (azucaradora_demorada) {
						logger << msg_header << "Esperando a la azucaradora asociada." << nl;
						mutex_llenadora.wait();
					} else {
						logger << msg_header << "Despertando a la azucaradora asociada." << nl;
						mutex_azucaradora.signal();
					}
					// Fin preprotocolo.

					jugo--;
					procesadas++;
					logger << msg_header << "Llenando botella " << entrante.id << "." << nl;
					// llenar botella.
					SIMULAR_LLENAR;

					// Postprotocolo de sincronización con la azucaradora.
					azucaradora_demorada = false;
					mutex_sync.wait();
						if (sync[boca] == 0)
							sync[boca] = 1;
						else
							sync[boca]--;
						azucaradora_demorada = ( sync[boca] != 0 );
					mutex_sync.signal();
					if (azucaradora_demorada) {
						logger << msg_header << "Esperando a la azucaradora asociada." << nl;
						mutex_llenadora.wait();
					} else {
						logger << msg_header << "Despertando a la azucaradora asociada." << nl;
						mutex_azucaradora.signal();
					}
					// Fin postprotocolo.

					// Enviar botella llena a la siguiente estación.
					logger << msg_header << "Botella llenada " << entrante.id;
					logger << " (" << procesadas << " en esta estación)." << nl;
					llena.id = entrante.id;
					llenadoras_salida.send(llena);
				} else {
					error << msg_header << "Ignorando respuesta incorrecta." << nl;
				}
			} else {
				logger << msg_header << "Enviando pedido de reposición de dosis de jugo." << nl;
				repositor_salida.send(pedir_jugo);
				logger << msg_header << "Detenida a la espera de dosis de jugo." << nl;
				mutex_llenadora.wait();
				reposicion.tipo = 0;
				repositor_salida.receive(reposicion,getpid());
				
				if ( (reposicion.tipo == getpid()) && (reposicion.recurso=JUGO) ) {
					jugo+= reposicion.cantidad;
					if (reposicion.cantidad > 0)
						logger << msg_header << "Se recibio un pack de " << reposicion.cantidad << " dosis de jugo." << nl;
					else
						logger << msg_header << "Se recibio un pack sin jugo." << nl;
				} else {
					error << msg_header << "Ignorando respuesta de reposición incorrecta." << nl;
				}
			}
			mutex_llenadora.signal();
		}

	} catch (ipc::exception& e) {
		logger << msg_header << e.what() << "." << nl;
		exit(-1);
	}
	logger << msg_header << "Fin." << nl;
	return 0;
}
